﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="NoiseMap.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2007, 2008, 2009, 2010, 2011
//       Roland Rosenkranz (Glatzemann@email.de)
//
//    Based on libnoise by Jason Bevins
//      Copyright (C) 2003, 2004 Jason Bevins (licensed under LGPL)
// </copyright>
// <license>
//   This file is part of starLiGHT.Noise.
//
//   starLiGHT.Noise is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Noise is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Noise. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Noise are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 10370                 $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-06-12 12:29:10 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.Noise.Noise
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Text;

    #endregion

    /// Implements a noise map, a 2-dimensional array of floating-point
    /// values.
    ///
    /// A noise map is designed to store coherent-noise values generated by a
    /// noise module, although it can store values from any source.  A noise
    /// map is often used as a terrain height map or a grayscale texture.
    ///
    /// The size (width and height) of the noise map can be specified during
    /// object construction or at any other time.
    ///
    /// The GetValue() and SetValue() methods can be used to access individual
    /// values stored in the noise map.
    ///
    /// This class manages its own memory.  If you copy a noise map object
    /// into another noise map object, the original contents of the noise map
    /// object will be freed.
    ///
    /// If you specify a new size for the noise map and the new size is
    /// smaller than the current size, the allocated memory will not be
    /// reallocated.
    /// Call ReclaimMem() to reclaim the wasted memory.
    ///
    /// <b>Border Values</b>
    ///
    /// All of the values outside of the noise map are assumed to have a
    /// common value known as the <i>border value</i>.
    ///
    /// To set the border value, call the SetBorderValue() method.
    ///
    /// The GetValue() method returns the border value if the specified value
    /// lies outside of the noise map.
    ///
    /// <b>Internal Noise Map Structure</b>
    ///
    /// Internally, the values are organized into horizontal rows called @a
    /// slabs.  Slabs are ordered from bottom to top.
    ///
    /// Each slab contains a contiguous row of values in memory.  The values
    /// in a slab are organized left to right.
    ///
    /// The offset between the starting points of any two adjacent slabs is
    /// called the <i>stride amount</i>.  The stride amount is measured by
    /// the number of @a float values between these two starting points, not
    /// by the number of bytes.  For efficiency reasons, the stride is often a
    /// multiple of the machine word size.
    ///
    /// The GetSlabPtr() and GetConstSlabPtr() methods allow you to retrieve
    /// pointers to the slabs themselves.
    public class NoiseMap
    {
        // The raster's stride length must be a multiple of this constant.
        private const int RASTER_STRIDE_BOUNDARY = 4;

        /// The maximum width of a raster.
        private const int RASTER_MAX_WIDTH = 32767;

        /// The maximum height of a raster.
        private const int RASTER_MAX_HEIGHT = 32767;

        /// Value used for all positions outside of the noise map.
        private float borderValue;

        /// The current height of the noise map.
        private int height;

        /// The amount of memory allocated for this noise map.
        ///
        /// This value is equal to the number of @a float values allocated for
        /// the noise map, not the number of bytes.
        private int memUsed;

        /// A pointer to the noise map buffer.
        private float[] noiseMap;

        /// The stride amount of the noise map.
        private int stride;

        /// The current width of the noise map.
        private int width;

        /// Constructor.
        ///
        /// Creates an empty noise map.
        public NoiseMap()
        {
        }

        /// Constructor.
        ///
        /// @param width The width of the new noise map.
        /// @param height The height of the new noise map.
        ///
        /// @pre The width and height values are positive.
        /// @pre The width and height values do not exceed the maximum
        /// possible width and height for the noise map.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        /// @throw noise::ExceptionOutOfMemory Out of memory.
        ///
        /// Creates a noise map with uninitialized values.
        ///
        /// It is considered an error if the specified dimensions are not
        /// positive.
        public NoiseMap(int width, int height)
        {
        }

        /// Copy constructor.
        ///
        /// @throw noise::ExceptionOutOfMemory Out of memory.
        public NoiseMap(NoiseMap rhs)
        {
        }

        /// Clears the noise map to a specified value.
        ///
        /// @param value The value that all positions within the noise map are
        /// cleared to.
        public void Clear(float value)
        {
        }

        /// Returns the value used for all positions outside of the noise map.
        ///
        /// @returns The value used for all positions outside of the noise
        /// map.
        ///
        /// All positions outside of the noise map are assumed to have a
        /// common value known as the <i>border value</i>.
        public float GetBorderValue()
        {
            return this.borderValue;
        }

        /// Returns a const pointer to a slab.
        ///
        /// @returns A const pointer to a slab at the position (0, 0), or
        /// @a NULL if the noise map is empty.
        public float[] GetConstSlabPtr()
        {
            return this.noiseMap;
        }

        /// Returns a const pointer to a slab at the specified row.
        ///
        /// @param row The row, or @a y coordinate.
        ///
        /// @returns A const pointer to a slab at the position (0, @a row),
        /// or @a NULL if the noise map is empty.
        ///
        /// @pre The coordinates must exist within the bounds of the noise
        /// map.
        ///
        /// This method does not perform bounds checking so be careful when
        /// calling it.
        public float GetConstSlabPtr(int row)
        {
            return this.GetConstSlabPtr(0, row);
        }

        /// Returns a const pointer to a slab at the specified position.
        ///
        /// @param x The x coordinate of the position.
        /// @param y The y coordinate of the position.
        ///
        /// @returns A const pointer to a slab at the position (@a x, @a y),
        /// or @a NULL if the noise map is empty.
        ///
        /// @pre The coordinates must exist within the bounds of the noise
        /// map.
        ///
        /// This method does not perform bounds checking so be careful when
        /// calling it.
        public float GetConstSlabPtr(int x, int y)
        {
            if ((int)x + (int)this.stride * (int)y <= this.noiseMap.Length)
                return this.noiseMap[(int)x + (int)this.stride * (int)y];
            else
                return this.noiseMap[this.noiseMap.Length];
        }

        /// Returns the height of the noise map.
        ///
        /// @returns The height of the noise map.
        public int GetHeight()
        {
            return this.height;
        }

        /// Returns the amount of memory allocated for this noise map.
        ///
        /// @returns The amount of memory allocated for this noise map.
        ///
        /// This method returns the number of @a float values allocated.
        public int GetMemUsed()
        {
            return this.memUsed;
        }

        /// Returns a pointer to a slab.
        ///
        /// @returns A pointer to a slab at the position (0, 0), or @a NULL if
        /// the noise map is empty.
        public float GetSlabPtr()
        {
            return this.noiseMap[0];
        }

        /// Returns a pointer to a slab at the specified row.
        ///
        /// @param row The row, or @a y coordinate.
        ///
        /// @returns A pointer to a slab at the position (0, @a row), or
        /// @a NULL if the noise map is empty.
        ///
        /// @pre The coordinates must exist within the bounds of the noise
        /// map.
        ///
        /// This method does not perform bounds checking so be careful when
        /// calling it.
        public float GetSlabPtr(int row)
        {
            return this.GetSlabPtr(0, row);
        }

        /// Returns a pointer to a slab at the specified position.
        ///
        /// @param x The x coordinate of the position.
        /// @param y The y coordinate of the position.
        ///
        /// @returns A pointer to a slab at the position (@a x, @a y) or
        /// @a NULL if the noise map is empty.
        ///
        /// @pre The coordinates must exist within the bounds of the noise
        /// map.
        ///
        /// This method does not perform bounds checking so be careful when
        /// calling it.
        public float GetSlabPtr(int x, int y)
        {
            return this.noiseMap[(int)x + (int)this.stride * (int)y];
        }

        /// <summary>
        /// Returns the stride amount of the noise map.
        /// </summary>
        /// <returns>
        /// The stride amount of the noise map.
        /// </returns>
        /// <remarks>
        /// - The <i>stride amount</i> is the offset between the starting points of any two adjacent slabs in a noise map.
        /// - The stride amount is measured by the number of @a float values between these two points, not by the number of bytes.
        /// </remarks>
        public int GetStride()
        {
            return this.stride;
        }

        /// Returns a value from the specified position in the noise map.
        ///
        /// @param x The x coordinate of the position.
        /// @param y The y coordinate of the position.
        ///
        /// @returns The value at that position.
        ///
        /// This method returns the border value if the coordinates exist
        /// outside of the noise map.
        public float GetValue(int x, int y)
        {
            if (this.noiseMap != null)
            {
                if (x >= 0 && x < this.width && y >= 0 && y < this.height)
                {
                    return this.GetConstSlabPtr(x, y);
                }
            }
            // The coordinates specified are outside the noise map.  Return the border
            // value.
            return this.borderValue;
        }

        /// Returns the width of the noise map.
        ///
        /// @returns The width of the noise map.
        public int GetWidth()
        {
            return this.width;
        }

        /// Reallocates the noise map to recover wasted memory.
        ///
        /// @throw noise::ExceptionOutOfMemory Out of memory.  (Yes, this
        /// method can return an out-of-memory exception because two noise
        /// maps will temporarily exist in memory during this call.)
        ///
        /// The contents of the noise map is unaffected.
        public void ReclaimMem()
        {
        }

        /// Sets the value to use for all positions outside of the noise map.
        ///
        /// @param borderValue The value to use for all positions outside of
        /// the noise map.
        ///
        /// All positions outside of the noise map are assumed to have a
        /// common value known as the <i>border value</i>.
        public void SetBorderValue(float borderValue)
        {
            this.borderValue = borderValue;
        }

        /// Sets the new size for the noise map.
        ///
        /// @param width The new width for the noise map.
        /// @param height The new height for the noise map.
        ///
        /// @pre The width and height values are positive.
        /// @pre The width and height values do not exceed the maximum
        /// possible width and height for the noise map.
        ///
        /// @throw noise::ExceptionInvalidParam See the preconditions.
        /// @throw noise::ExceptionOutOfMemory Out of memory.
        ///
        /// On exit, the contents of the noise map are undefined.
        ///
        /// If the @a OUT_OF_MEMORY exception occurs, this noise map object
        /// becomes empty.
        ///
        /// If the @a INVALID_PARAM exception occurs, the noise map is
        /// unmodified.
        public void SetSize(int width, int height)
        {
            if (width < 0 || height < 0 || width > RASTER_MAX_WIDTH || height > RASTER_MAX_HEIGHT)
            {
                // Invalid width or height.
                throw new Exception("invalid param");
            }
            else if (width == 0 || height == 0)
            {
                // An empty noise map was specified.  Delete it and zero out the size
                // member variables.
                this.DeleteNoiseMapAndReset();
            }
            else
            {
                // A new noise map size was specified.  Allocate a new noise map buffer
                // unless the current buffer is large enough for the new noise map (we
                // don't want costly reallocations going on.)
                int newMemUsage = this.CalcMinMemUsage(width, height);
                if (this.memUsed < newMemUsage)
                {
                    // The new size is too big for the current noise map buffer.  We need to
                    // reallocate.
                    this.DeleteNoiseMapAndReset();
                    this.noiseMap = new float[newMemUsage];
                    this.memUsed = newMemUsage;
                }
                this.stride = (int)this.CalcStride(width);
                this.width = width;
                this.height = height;
            }
        }

        /// Sets a value at a specified position in the noise map.
        ///
        /// @param x The x coordinate of the position.
        /// @param y The y coordinate of the position.
        /// @param value The value to set at the given position.
        ///
        /// This method does nothing if the noise map object is empty or the
        /// position is outside the bounds of the noise map.
        public void SetValue(int x, int y, float value)
        {
            if (this.noiseMap != null)
            {
                if (x >= 0 && x < this.width && y >= 0 && y < this.height)
                {
                    this.noiseMap[(int)x + (int)this.stride * (int)y] = value;
                }
            }
        }

        /// Takes ownership of the buffer within the source noise map.
        ///
        /// @param source The source noise map.
        ///
        /// On exit, the source noise map object becomes empty.
        ///
        /// This method only moves the buffer pointer so this method is very
        /// quick.
        public void TakeOwnership(NoiseMap source)
        {
        }

        /// Returns the minimum amount of memory required to store a noise map
        /// of the specified size.
        ///
        /// @param width The width of the noise map.
        /// @param height The height of the noise map.
        ///
        /// @returns The minimum amount of memory required to store the noise
        /// map.
        ///
        /// The returned value is measured by the number of @a float values
        /// required to store the noise map, not by the number of bytes.
        private int CalcMinMemUsage(int width, int height)
        {
            return this.CalcStride((int)width) * (int)height;
        }

        /// <summary>
        /// Calculates the stride amount for a noise map.
        /// </summary>
        /// <param name="width">
        /// The width of the noise map.
        /// </param>
        /// <returns>
        /// The stride amount.
        /// </returns>
        /// <remarks>
        /// - The <i>stride amount</i> is the offset between the starting points of any two adjacent slabs in a noise map.
        /// - The stride amount is measured by the number of @a float values between these two points, not by the number of bytes.
        /// </remarks>
        private int CalcStride(int width)
        {
            return (int)(((width + RASTER_STRIDE_BOUNDARY - 1)
              / RASTER_STRIDE_BOUNDARY) * RASTER_STRIDE_BOUNDARY);
        }

        /// Copies the contents of the buffer in the source noise map into
        /// this noise map.
        ///
        /// @param source The source noise map.
        ///
        /// @throw noise::ExceptionOutOfMemory Out of memory.
        ///
        /// This method reallocates the buffer in this noise map object if
        /// necessary.
        ///
        /// @warning This method calls the standard library function
        /// @a memcpy, which probably violates the DMCA because it can be used
        //. to make a bitwise copy of anything, like, say, a DVD.  Don't call
        /// this method if you live in the USA.
        private void CopyNoiseMap(NoiseMap source)
        {
        }

        /// Resets the noise map object.
        ///
        /// This method is similar to the InitObj() method, except this method
        /// deletes the buffer in this noise map.
        private void DeleteNoiseMapAndReset()
        {
            this.noiseMap = null;
            this.InitObj();
        }

        /// Initializes the noise map object.
        ///
        /// @pre Must be called during object construction.
        /// @pre The noise map buffer must not exist.
        private void InitObj()
        {
            this.noiseMap = null;
            this.height = 0;
            this.width = 0;
            this.stride = 0;
            this.memUsed = 0;
            this.borderValue = 0.0f;
        }
    }
}

